Python binding for Birdee
Birdee depends on Python language for compile-time scripts that can generate and modify the existing program to compile. The Birdee compiler provides various functions and class for Python code that is embeded in the Birdee program, to enable them to access the internal data structures and functions in the Birdee compiler. The set of Python interfaces provided by Birdee is called "Python binding". This series of documents lists the details of how Python embeded scripts can use the interfaces of the Birdee compilers.
First, we will discuss overview of the usage of Python scripts in Birdee code. The Python scripts can be invoked in three ways:
-
- By placing them between a pair of
{@ ... @}
. Here the script will serve as an expression, statement or a type in Birdee code
- By placing them between a pair of
-
- By adding an annotation on an expression or statement. The python function specified by the annotation will be called
-
- By writing a generative script to generate a Birdee program by an indenpendent Python program(not embeded in Birdee code)
In the above apporaches, the Python binding is available for the scripts. You can use the script to
- Insert code to the existing program being compiled (mainly in approaches 1 and 3)
- Alter the existing code (mainly in approach 2)
The details of the basics for Birdee scripts have been discussed in the chapter "Compile-time scripts" in Birdee language mannual.
The rest part of this section will talk about the Birdee compiler internals that are related to Python binding and how the Python binding can be invoked.
How Birdee compiler parses a source file?
A Birdee program is composed of a list of statements. A simple statement can be a line of code like
dim a as int
Some statements can hold other statements. For example, the syntax for "if" statement is:
if cond then
...
else
...
end
So in an "if", there will be a statement for the condition, two lists of statements for the both branches. It is natural to store a parsed Birdee program with a tree, since a statement can hold sub-statements. The tree representing the source code is called an Abstract Syntax Tree (AST). Each node of the tree is a statement in the source code. For example, an "if" node in AST has the structure like:
IF
- Cond
- List-True
- ...
- List-False
- ...
An important part of Python binding of Birdee is the binding for the AST. In Python scripts, the AST for different kinds of statements are wrapped into different Python classes. Developers can access the fields of the AST in the scripts via these Python classes.
In Birdee, some statements may have a "returning value". e.g. The statement add(1,2)
returns the function call result. And other statements does not have a returning value. The kinds of statements that may return a value is called "expression". In Birdee's AST, an expression is a sub-class of the statement class. All kinds of AST (e.g. functions, variables definitions, class definitions, ...) are sub-classes of either expression class (called ExprAST in Python binding), or statement class (StatementAST).
How the Python binding can be invoked
Most of binding interfaces of Birdee can be accessed via the Python module "birdeec". For embeded scripts and annotations, all symbols in this module has already been imported. For generative scripts, you need to manually import this Python module.
The binding falls into two catagories. The first is the supporting functions that control the Birdee compiler. e.g. the expr
function will call the Birdee compiler to generate an AST by the code given by the Python string. The second part of the binding is the AST classes for different kinds of statements. In each of the AST classes, you can read and write to the internal properties of the nodes to inspect and modify the Birdee program.
Based on the Python binding, we also provide some higher level APIs that can simplify the operations of the scripts.